﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using AzukiMap.Tools.Logger;
using System.Collections.Generic;
using AzukiMap.Layer;
using System.Collections.ObjectModel;

namespace AzukiMap.Map
{
    public delegate void ScreenScaleChangedHandler(object sender, ScreenScaleChangedArgs e);
    public delegate void LayerContainerMovedHandler(object sender, LayerContainerMovedArgs e);
    public delegate void MouseMovedOnScreenHandler(object sender, MouseMovedOnScreenArgs e);

    public partial class MapMouseControl
    {
        #region events

        public event ScreenScaleChangedHandler ScreenScaleChanged;
        public event LayerContainerMovedHandler LayerContainerMoved;
        public event MouseMovedOnScreenHandler MouseMovedOnScreen;

        protected virtual void OnContainerMoved(LayerContainerMovedArgs e)
        {
            if (LayerContainerMoved != null)
            {
                LayerContainerMoved(this, e);
            }
        }

        protected virtual void OnScaleChanged(ScreenScaleChangedArgs e)
        {
            if (ScreenScaleChanged != null)
            {
                ScreenScaleChanged(this, e);
            }
        }

        protected virtual void OnMouseMoved(MouseMovedOnScreenArgs e)
        {
            if (MouseMovedOnScreen != null)
            {
                MouseMovedOnScreen(this, e);
            }
        }

        #endregion

        # region fields

        const double ZOOM_IN_RATE = 1.2;
        const double ZOOM_OUT_RATE = 0.8;
        const long ZOOM_FINISHED_TIMERAG = 10000000; // 1 sec

        Canvas _rootCanvas;
        Canvas _layerContainer;

        //ObservableCollection<ILayer> _layers; 
        //List<ILayer> _layers;
        Point _dragStartPoint;
        Point _currentPoint;
        bool _isMoving;
        bool _isLocked;

        double _adjX;
        double _adjY;

        TransformGroup _layerTransform;
        TranslateTransform _moveTransform;
        ScaleTransform _scaleTransform;

        MouseWheelHelper _wheelControl;
        DispatcherTimer _wheelControlFinishTimer;

        #endregion

        #region properties

        public bool IsLocked
        {
            get { return _isLocked; }
            set { _isLocked = value; }
        }

        public Canvas LayerContainer
        {
            get { return _layerContainer; }
            set { _layerContainer = value; }
        }
        #endregion

        #region constructor

        //public MapLayerControl(Canvas rootCanvas, Canvas layerContainer)
        internal MapMouseControl(Canvas rootCanvas)
        {
            Logger.All("MapMouseControl.MapMouseControl: init start.");

            _rootCanvas = rootCanvas;
            //_layerContainer = layerContainer;
            _layerContainer = PrepareLayerContainer();
            //_layers = new List<ILayer>();
            //_layers = new ObservableCollection<ILayer>();
            AddRootCanvasEvents();

            _moveTransform = new TranslateTransform();
            _scaleTransform = new ScaleTransform();
            _layerTransform = new TransformGroup();
            _layerTransform.Children.Add(_moveTransform);
            _layerTransform.Children.Add(_scaleTransform);

            _moveTransform.X = 0;
            _moveTransform.Y = 0;
            _scaleTransform.ScaleX = 1.0;
            _scaleTransform.ScaleY = 1.0;

            _isMoving = false;
            _isLocked = false;
            _dragStartPoint = new Point(0, 0);

            _wheelControlFinishTimer = new DispatcherTimer();
            _wheelControlFinishTimer.Tick += new EventHandler(WheelControlFinishTimer_Tick);

            _adjX = 0;
            _adjY = 0;

            Logger.All("MapMouseControl.MapMouseControl: init finish.");
        }

        #endregion

        #region methods

        Canvas PrepareLayerContainer()
        {
            Logger.All("MapControl.PrepereLayerContainer");

            Canvas layerContainer = new Canvas();

            // TODO this is test. should remove.
            Brush blue = new SolidColorBrush(Colors.Blue);
            layerContainer.Background = blue;

            _rootCanvas.Children.Add((UIElement)layerContainer);

            return layerContainer;
        }

        /*
        internal void AddLayer(ILayer mapLayer)
        {
            _layerContainer.Children.Add((UIElement)mapLayer.MapCanvas);
            _layers.Add(mapLayer);
        }
         */

        internal void MoveTo(Point p)
        {
            if (_isMoving && !_isLocked)
            {
                Logger.All("MapMouseControl.MoveTo: " + p.ToString());

                if (_wheelControlFinishTimer.IsEnabled)
                    _wheelControlFinishTimer.Stop();

                double moveX = _dragStartPoint.X - p.X;
                double moveY = _dragStartPoint.Y - p.Y;
                _moveTransform.X = _adjX - moveX;
                _moveTransform.Y = _adjY - moveY;
                Transform();
            }
        }

        void ZoomByWheel(double delta)
        {
            if (!_isLocked)
            {
                if (_wheelControlFinishTimer.IsEnabled)
                    _wheelControlFinishTimer.Stop();

                double zoom = 1.0;
                if (delta < 0)
                {
                    zoom *= ZOOM_OUT_RATE;
                }
                else
                {
                    zoom *= ZOOM_IN_RATE;
                }

                _scaleTransform.ScaleX *= zoom;
                _scaleTransform.ScaleY *= zoom;
                _scaleTransform.CenterX = _currentPoint.X;
                _scaleTransform.CenterY = _currentPoint.Y;

                Logger.All(
                    "MapMouseControl.ZoomByWheel: " 
                    + " delta = " + delta.ToString()
                    + " zoom = " + zoom.ToString()
                    + " _currentPoint = " + _currentPoint.ToString()
                    );

                Transform();
                _wheelControlFinishTimer.Interval = new TimeSpan(ZOOM_FINISHED_TIMERAG);
                _wheelControlFinishTimer.Start();
            }
        }

        void Transform()
        {
            Logger.All("MapMouseControl.Transform"); 

            _layerContainer.RenderTransform = _layerTransform;
        }

        void MoveFinished(Point p)
        {
            double moveX = _dragStartPoint.X - p.X;
            double moveY = _dragStartPoint.Y - p.Y;
            if (moveX == 0 && moveY == 0)
            {
                return;
            }

            Logger.All(
               "MapMouseControl.MoveFinished: "
                + " p = " + p.ToString()
                + " moveX = " + moveX.ToString()
                + " moveY = " + moveY.ToString()
            );

            LayerContainerMovedArgs e = new LayerContainerMovedArgs(moveX, moveY,_adjX, _adjY);
            OnContainerMoved(e);
            //DrawAll();
        }

        void ZoomFinished()
        {
            double anchorX = _scaleTransform.CenterX;
            double anchorY = _scaleTransform.CenterY;
            double scaleRatio = _scaleTransform.ScaleX;

            Logger.All(
               "MapMouseControl.ZoomFinished: "
                + " anchorX = " + anchorX.ToString()
                + " anchorY = " + anchorY.ToString()
                + " scaleRatio = " + scaleRatio.ToString()
            );

            ScreenScaleChangedArgs e = new ScreenScaleChangedArgs(scaleRatio, anchorX, anchorY, 0, 0);
            OnScaleChanged(e);

            Refresh();
            //DrawAll();
        }

        internal void Refresh()
        {
            Logger.All("MapMouseControl.Refresh");

            ResetTransforms();
            //RemoveAllDrawings();
        }

        void ResetTransforms()
        {
            _scaleTransform.ScaleX = 1.0;
            _scaleTransform.ScaleY = 1.0;
            _moveTransform.X = 0;
            _moveTransform.Y = 0;
            _adjX = 0;
            _adjY = 0;
        }


        void AddRootCanvasEvents()
        {
            _wheelControl = new MouseWheelHelper(_rootCanvas);
            _wheelControl.Moved += new EventHandler<MouseWheelEventArgs>(RootCanvas_WheelMoved);

            _rootCanvas.MouseLeftButtonDown
                += new MouseButtonEventHandler(RootCanvas_MouseLeftButtonDown);
            _rootCanvas.MouseLeftButtonUp
                += new MouseButtonEventHandler(RootCanvas_MouseLeftButtonUp);
            _rootCanvas.MouseMove
                += new MouseEventHandler(RootCanvas_MouseMove);
        }

        void MouseLeftButtonDown(Point p)
        {
            Logger.All(
                "MapMouseControl.MouseLeftButtonDown:"
                + " p = " + p.ToString()
                ); 

            _isMoving = true;
            _dragStartPoint = p;
        }

        void MouseMove(Point p)
        {
            Logger.All(
                "MapMouseControl.MouseMove:"
                + " p = " + p.ToString()
                );

            if (_isMoving && !_isLocked)
            {
                MoveTo(p);
            }

            if (MouseMovedOnScreen != null)
            {
                OnMouseMoved(new MouseMovedOnScreenArgs(p.X, p.Y));
            }
        }

        void MouseLeftButtonUp(Point p)
        {
            Logger.All(
                "MapMouseControl.MouseMove:"
                + " p = " + p.ToString()
                ); 

            MoveTo(p);
            _adjX = _moveTransform.X;
            _adjY = _moveTransform.Y;
            _isMoving = false;
            MoveFinished(p);
        }
        void RootCanvas_WheelMoved(object sender, MouseWheelEventArgs e)
        {
            double delta = e.Delta;
            ZoomByWheel(delta);
        }

        void RootCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            Point pos = e.GetPosition((UIElement)sender);
            _currentPoint = pos;
            MouseMove(pos);
        }

        void RootCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Point pos = e.GetPosition((UIElement)sender);
            MouseLeftButtonUp(pos);
        }

        void RootCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point pos = e.GetPosition((UIElement)sender);
            MouseLeftButtonDown(pos);
        }

        void WheelControlFinishTimer_Tick(object sender, EventArgs e)
        {
            _wheelControlFinishTimer.Stop();
            ZoomFinished();
        }

        #endregion


    }


}
